#include "StdAfx.h"

CWindow* Singleton<CWindow>::m_instance = 0;
Mouse* Singleton<Mouse>::m_instance = 0;
std::map<HWND, CWindow*> CWindow::sWindows;

CWindow::CWindow(ui32 x, ui32 y, ui32 dx, ui32 dy)
{
	m_posx = x;
	m_posy = y;
	m_sizex = dx;
	m_sizey = dy;

	mCurAnim = getAnimatedCursor(IDR_ANICUR1);
	mCurStandard = LoadCursor(NULL, IDC_ARROW);
	mAnimatedCursor = true;
	mMouseInClient = true;

	ZeroMemory(&m_wndclass, sizeof(WNDCLASSEX));
	m_wndclass.cbClsExtra = 0;
	m_wndclass.cbSize = sizeof(WNDCLASSEX);
	m_wndclass.cbWndExtra = 0;
	m_wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	m_wndclass.hCursor = LoadCursor(0, IDC_ARROW);
	m_wndclass.hIcon = (HICON)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(102), IMAGE_ICON, LR_DEFAULTSIZE, LR_DEFAULTSIZE, LR_DEFAULTCOLOR);
	m_wndclass.hCursor = NULL;
	//LoadCursor(0, IDC_ARROW);//(HICON)LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(103), IMAGE_CURSOR, LR_DEFAULTSIZE, LR_DEFAULTSIZE, LR_DEFAULTCOLOR);
	
	m_wndclass.hIconSm = 0;
	m_wndclass.hInstance = GetModuleHandle(0);
	m_wndclass.lpfnWndProc = &CWindow::WndProc;
	m_wndclass.lpszClassName = "_YEDIT_DX";
	m_wndclass.lpszMenuName = 0;
	m_wndclass.style = CS_HREDRAW | CS_VREDRAW;
	
	RegisterClassEx(&m_wndclass);

	mAspectRatio = (float)dx / (float)dy;

	m_hWindow = CreateWindowEx(0, "_YEDIT_DX", "YiasEdit - DirectX 9.0c", WS_SYSMENU | WS_OVERLAPPED | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_MAXIMIZE | WS_THICKFRAME, m_posx, m_posy, m_sizex, m_sizey,
		0, 0, GetModuleHandle(0), this);
	try
	{
		new D3DManager(this);
	}
	catch(std::exception& e)
	{
		StartupException ex;
		ex.szTitle = "3D error";
		ex.szBody = e.what();
		throw e;
	}
	m_lastRender = GetTickCount();
	setCursor(true);
	TRACKMOUSEEVENT tme = { sizeof(tme) };
	tme.dwFlags = TME_LEAVE;
	tme.hwndTrack = m_hWindow;
	TrackMouseEvent(&tme);
}

void CWindow::setCursor(bool animated) {
	if(animated) {
		SetCursor(mCurAnim);
	} else {
		SetCursor(mCurStandard);
	}

	mAnimatedCursor = animated;
}

void HandleMessage(MSG& msg)
{
	TranslateMessage(&msg);
	DispatchMessage(&msg);
}

void CWindow::BeginShow()
{
	ShowWindow(m_hWindow, SW_SHOW);
	UpdateWindow(m_hWindow);
	m_running = true;

	WINDOWINFO inf;
	GetWindowInfo(m_hWindow, &inf);
	m_clientY = inf.rcClient.top;
	m_clientX = inf.rcClient.left;
	new Mouse();

	while(m_running)
	{
		MSG msg;
		while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
		{
			HandleMessage(msg);
		}
		sD3D.Render();
	}
}

static bool gFirstResize = true;

LRESULT __stdcall CWindow::WndProc(HWND hWindow, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	try
	{
		switch(uMsg)
		{
		case WM_CREATE:
			{
				LPCREATESTRUCT cr = (LPCREATESTRUCT)lParam;
				if(cr && cr->lpCreateParams)
				{
					sWindows[hWindow] = (CWindow*)cr->lpCreateParams;
				}
				break;
			}
		case WM_DESTROY:
			{
				if(sWindows[hWindow])
				{
					sWindows[hWindow]->m_running = false;
				}
				break;
			}
		case WM_RBUTTONDOWN:
		case WM_RBUTTONUP:
			{
				sCamera.UpdatePos(Vertex2F(LOWORD(lParam), HIWORD(lParam)));
				break;
			}
		case WM_LBUTTONDOWN:
			{
				if(sWindows[hWindow])
				{
					if(!sUI.OnMouseActivity(Vertex2F(LOWORD(lParam), HIWORD(lParam)), false));
						//sModelSel.OnMouseDown(Vertex2F(LOWORD(lParam), HIWORD(lParam)));
					sCamera.UpdatePos(Vertex2F(LOWORD(lParam), HIWORD(lParam)));
				}
				sMouse.OnMouseDown(true);
				break;
			}
		case WM_LBUTTONUP:
			{
				if(sWindows[hWindow])
				{
					if(!sUI.OnMouseActivity(Vertex2F(LOWORD(lParam), HIWORD(lParam)), true))
					{
						//if(!sModelSel.OnMouseUp(Vertex2F(LOWORD(lParam), HIWORD(lParam))));
							sD3D.OnMouseClick(Vertex2F(LOWORD(lParam), HIWORD(lParam)), true, true);
					}
					sCamera.UpdatePos(Vertex2F(LOWORD(lParam), HIWORD(lParam)));
				}
				sMouse.OnMouseUp(true);
				break;
			}
		case WM_MOUSEMOVE:
			{
				sWindow.onMouseMove();
				sMouse.OnMouseMove(LOWORD(lParam), HIWORD(lParam));
				sSelection.SetMouseMove(LOWORD(lParam), HIWORD(lParam));
				if(sWindows[hWindow])
				{
					if(wParam & MK_RBUTTON)
						sMainMenu.OnMouseMove(Vertex2F(LOWORD(lParam), HIWORD(lParam)), true);
					else
						sMainMenu.OnMouseMove(Vertex2F(LOWORD(lParam), HIWORD(lParam)), false);
					sUI.OnMouseMove(Vertex2F(LOWORD(lParam), HIWORD(lParam)));
					if(wParam & MK_RBUTTON)
					{
						//if(!sModelSel.OnMouseMove(Vertex2F(LOWORD(lParam), HIWORD(lParam))))
							sCamera.OnMouseMove(Vertex2F(LOWORD(lParam), HIWORD(lParam)));
					}
					//else
					//	sModelSel.UpdatePos(Vertex2F(LOWORD(lParam), HIWORD(lParam)));
				}
				break;
			}
		case WM_MOUSEWHEEL:
			{
				i16 delta = HIWORD(wParam);
				sMainMenu.MouseWheel(delta);
				sCamera.OnMouseWheel(delta);
				break;
			}
		case WM_CHAR:
			{
				if(InputBox::GetInstance())
					sInput.OnKey(wParam);
				sUI.OnChar(wParam);
				sSelection.OnKey(wParam);
				break;
			}
		case WM_KEYDOWN:
			{
				if(InputBox::GetInstance())
					sInput.OnVirtualKey(wParam);
				sSelection.OnVirtualKey(wParam);
				break;
			}
		case WM_KEYUP:
			switch(wParam)
			{
			case VK_SNAPSHOT:
				{
					//sD3D.ScreenShot();
					break;
				}
			}
			break;
		case WM_MOVE:
			{
				if(sWindows[hWindow])
				{
					sWindows[hWindow]->m_posx = LOWORD(lParam);
					sWindows[hWindow]->m_posy = HIWORD(lParam);
					WINDOWINFO inf;
					GetWindowInfo(sWindows[hWindow]->m_hWindow, &inf);
					sWindows[hWindow]->m_clientY = inf.rcClient.top;
					sWindows[hWindow]->m_clientX = inf.rcClient.left;
				}
				break;
			}
		case WM_CLOSE:
			{
				sD3D.OnClose();
				DestroyWindow(hWindow);
				return 1;
			}
		case WM_SIZING:
			{
				LPRECT rc = (LPRECT)lParam;
				i32 width = rc->right - rc->left;
				i32 height = rc->bottom - rc->top;
				i32 res = (i32)wParam;
				if (res == WMSZ_LEFT || res == WMSZ_RIGHT) {
					rc->bottom = rc->top + (int)(width / sWindow.mAspectRatio);
				}
				else if (res == WMSZ_TOP || res == WMSZ_BOTTOM) {
					rc->right = rc->left + (int)(sWindow.mAspectRatio * height);
				}
				else if (res == WMSZ_RIGHT + WMSZ_BOTTOM) {
					rc->bottom = rc->top + (int)(width / sWindow.mAspectRatio);
				}
				else if (res == WMSZ_LEFT + WMSZ_TOP) {
					rc->left = rc->right - (int)(sWindow.mAspectRatio * height);
				}

				break;
			}
		case WM_SIZE:
			{
				if(gFirstResize)
				{
					gFirstResize = false;
					break;
				}
				//if(sD3D.GetAppState() == STATE_WORLD)
				//	break;
				if(wParam != SIZE_RESTORED && wParam != SIZE_MAXIMIZED)
					break;

				sD3D.ChangeSize();
				break;
			}

		case WM_MOUSELEAVE:
			{
				sWindow.mMouseInClient = false;
				break;
			}
		}
	}
	catch(...)
	{
	}
	return DefWindowProc(hWindow, uMsg, wParam, lParam);
}

HCURSOR CWindow::getAnimatedCursor(ui32 id) {
	HINSTANCE hInst = GetModuleHandle(NULL);
	HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(id), "ANICUR");
	DWORD dwSize = SizeofResource(hInst, hRes);
	HGLOBAL hGlob = LoadResource(hInst, hRes);
	LPBYTE pBytes = (LPBYTE)LockResource(hGlob); 

	return (HCURSOR)CreateIconFromResource(pBytes, dwSize, FALSE, 0x00030000);
}

void CWindow::handleMouseEnter() {
	setCursor(mAnimatedCursor);
}

void CWindow::onMouseMove() {
	if(!mMouseInClient) {
		handleMouseEnter();
		mMouseInClient = true;
		TRACKMOUSEEVENT tme = { sizeof(tme) };
		tme.dwFlags = TME_LEAVE;
		tme.hwndTrack = m_hWindow;
		TrackMouseEvent(&tme);
	}
}